package com.lingfengerick.commons.encrypt.sm2tool;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Base64;

import java.security.*;
import java.util.HashMap;
import java.util.Map;

/**
 * 国密SM2算法
 * @author lingfengerick
 * @since 2022/9/20 15:34
 */
public class SM2SecretStrategy{

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    /**
     * 指定密钥进行加密
     *
     * @param dataStr   需要加密的数据
     * @param publicKeyStr 加密密钥
     * @return 返回加密数据
     * @author lingfengerick
     * @since 2022/9/20  14:24
     */
    public static String encryptDataToString(String dataStr, String publicKeyStr) {
        ECPublicKeyParameters localECPublicKeyParameters = null;
        if (StringUtils.isEmpty(publicKeyStr)){
            return null;
        }
        PublicKey publicKey = SM2KeyPair.createPublicKey(publicKeyStr);
        if (publicKey instanceof BCECPublicKey) {
            BCECPublicKey localECPublicKey = (BCECPublicKey) publicKey;
            ECParameterSpec localECParameterSpec = localECPublicKey.getParameters();
            ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),
                    localECParameterSpec.getG(), localECParameterSpec.getN());
            localECPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), localECDomainParameters);
        }
        SM2Engine localSM2Engine = new SM2Engine();
        localSM2Engine.init(true, new ParametersWithRandom(localECPublicKeyParameters, new SecureRandom()));
        byte[] arrayOfByte;
        try {
            arrayOfByte = localSM2Engine.processBlock(dataStr.getBytes(), 0, dataStr.getBytes().length);
            return Base64.toBase64String(arrayOfByte);
        } catch (InvalidCipherTextException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
     * 指定密钥解密
     *
     * @param encryDataStr 加密数据
     * @param privateKeyStr    密钥
     * @return 解密数据
     * @author lingfengerick
     * @since 2022/9/20  14:28
     */
    public static String decryptDataToString(String encryDataStr, String privateKeyStr) {
        SM2Engine localSM2Engine = new SM2Engine();
        if (StringUtils.isEmpty(privateKeyStr)){
            return null;
        }
        PrivateKey privateKey = SM2KeyPair.createPrivateKey(privateKeyStr);
        BCECPrivateKey sm2PriK = (BCECPrivateKey) privateKey;
        ECParameterSpec localECParameterSpec = sm2PriK.getParameters();
        ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),
                localECParameterSpec.getG(), localECParameterSpec.getN());
        ECPrivateKeyParameters localECPrivateKeyParameters = new ECPrivateKeyParameters(sm2PriK.getD(),
                localECDomainParameters);
        localSM2Engine.init(false, localECPrivateKeyParameters);
        try {
            byte[] arrayOfByte = localSM2Engine.processBlock(Base64.decode(encryDataStr), 0, Base64.decode(encryDataStr).length);
            return new String(arrayOfByte);
        } catch (InvalidCipherTextException e) {
            e.printStackTrace();
            return null;
        }
    }
    /**
    * 生成公私密钥对
    * @author  lingfengerick
    * @since  2022/9/21  14:49
    * @return  密钥对
    *
    */
    public static Map<String , String> createKeyPair(){
        Map<String , String> keyPairMap = new HashMap<>();
        try {
            KeyPair keyPair = SM2KeyPair.generateKeyPair("EC");
            String publicKey = Base64.toBase64String(keyPair.getPublic().getEncoded());
            String privateKey = Base64.toBase64String(keyPair.getPrivate().getEncoded());
            keyPairMap.put("publicKey" , publicKey);
            keyPairMap.put("privateKey" , privateKey);
        } catch (Exception e) {
            keyPairMap = null;
        }
        return keyPairMap;
    }

    /**
     * 私钥签名
     * @param data 签名数据
     * @param privateKey 私钥对象
     * @return 签名信息
     * @throws Exception 异常
     */
    public static String signByPrivateKey(String data, PrivateKey privateKey) throws Exception {
        Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);
        sig.initSign(privateKey);
        sig.update(data.getBytes());
        byte[] ret = sig.sign();
        return Base64.toBase64String(ret);
    }

    /**
     * 公钥验签
     * @param data 签名数据
     * @param publicKey 公钥对象
     * @param signature 签名
     * @return 验签是否通过
     * @throws Exception 异常
     */
    public static boolean verifyByPublicKey(String data, PublicKey publicKey, String signature) throws Exception {
        Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);
        sig.initVerify(publicKey);
        sig.update(data.getBytes());
        return sig.verify(Base64.decode(signature));
    }
}
